{$X-}   {Turn off stack expansion. This is a Lisa concept, not needed on Mac}
{$U-}   {Turn off the Lisa Libraries. This is required by the WorkShop}
{$R-}   {Turn off range checking}

PROGRAM TextThings;

{Jeffery J. Bradford    Macintosh Technical Support  April 1985 }

{ This example shows you how to use the TextEdit routines to enter, delete}
{ and manipulate text. For more information on handling text look at the  }
{ example called EDIT and for scrolling look at the SCROLLING.TEXT example}
{ If you need more versitality in manipulating text look into the Munger  }
{ utility located in the ToolBox Utility Section.                         }

{  Specifics of this example are:                                         }
{     Entering text;                                                      }
{     Simple auto-scrolling routine                                       }
{     Changing cursor to an I-Beam when in view rectangle;                }
{     Setup Menus & Command Keys for Cutting, Copying, & Pasting text;    }


USES
    {$U Obj/Memtypes  } MemTypes,
    {$U Obj/QuickDraw } QuickDraw,
    {$U Obj/OSIntf    } OSIntf,
    {$U Obj/ToolIntf  } ToolIntf,
    {$U Obj/PackIntf  } PackIntf;

CONST
{menu stuff}
    AppleMenu = 256;
    FileMenu  = 257;
    EditMenu  = 258;

{text edit scrolling stuff}
    maxViewLines = 7;           {max lines that can be viewed}

{window IDs}
    WindResID = 256;

TYPE
{this is useful stuff you might need sometime}

    WordStuff = Packed Record
       Case Integer of
         0: (Word: Integer);
         1: (SByte1,SByte0: SignedByte);
         2: (b15,b14,b13,b12,b11,b10,b9,b8,b7,b6,b5,b4,b3,b2,b1,b0: Boolean)
       End;

    CharStuff = Packed Record
         chr3,chr2,chr1,chr0: char;
       End;

    LMwordPtr = ^Integer;          {pointer to low memory address}
    LMLongPtr = ^LongInt;          {pointer to low memory address - long}



VAR
{global program stuff}
    Finished:      Boolean;    {used to terminate the program}
    ClockCursor:   CursHandle; {handle to the waiting watch cursor}
    TextCursor:    CursHandle; {handle to the I - beam text entry cursor}

{text edit stuff}
    TextHandle: TEHandle;    {handle to the text record where all text is kept}
    DestRect:   Rect;        {Text view rect, used for updating}
    TextFrame:  Rect;        {holds the frame around the text}
    LastLine:   Integer;     {holds the last line showing on screen}

{Screen stuff}
    DragArea:   Rect;          {holds the area where window can be dragged in}
    GrowArea:   Rect;          {holds the area to which a window's size can change}
    Screen:     Rect;          {holds the screen dimensions}

{Window stuff}
    TextWindow:   WindowPtr;    {pointer to the first window}

{-----------------------------------------------------------------------------
                     end of global variable definition
-----------------------------------------------------------------------------}

PROCEDURE FrameText;
Begin
  FrameRect(TextFrame);
End;

{-----------------------------------------------------------------------------}

PROCEDURE ChangeCursorForText;
Var MousePt: Point;
Begin
  If TextWindow = FrontWindow then        {is it our window}
  begin
    GetMouse(MousePt);                    {get the mouse location}
    If PtinRect(MousePt,TextFrame)        {is it in the text view rect??}
       then SetCursor(TextCursor^^)       {if so set cursor to I-beam}
       else SetCursor(arrow);             {else make it an arrow}
  end;
End;

{-----------------------------------------------------------------------------}

PROCEDURE Scroll_ifNecessary(TxHdl:TEHandle);
{This is a very simple scrolling routine. Look at example/File.text or}
{one of the scrolling examples for more information. For this to work }
{better, check to see if the RETURN key was pressed along w/new lines.}

Var numLines: integer;     {number of lines in the text record}
    LnHeight: integer;     {line height of the font}

Begin
  numLines := TxHdl^^.nLines;
  LnHeight := TxHdl^^.LineHeight;

    If numLines >= maxViewLines then      {if text lines > 5 start checking}
    begin
       If numLines > LastLine then     {if lines > last count then scroll down}
       begin
         TEScroll(0,-LnHeight,TxHdl);
         LastLine := LastLine + 1;
       end;

       If numLines < LastLine then     {if lines < last count scroll up}
       begin
         TEScroll(0,+LnHeight,TxHdl);
         LastLine := LastLine - 1;
       end;
    end;
End;

{-----------------------------------------------------------------------------}

PROCEDURE ProcessMenu_in(CodeWord:longint);
Var
  Menu_No:    integer;        {menu number that was selected}
  Item_No:    integer;        {item in menu that was selected}
  NameHolder: Str255;         {name holder for desk accessory or font}
  DNA:        integer;        {OpenDA will never return 0, so don't care}

Begin
  If CodeWord <> 0 then {go ahead and process the command}
  begin
    Menu_No := HiWord(CodeWord);   {get the Hi word of...}
    Item_no := LoWord(CodeWord);   {get the Lo word of...}

    Case Menu_No of

     AppleMenu: Begin
                  GetItem(GetMHandle(AppleMenu), Item_No, NameHolder);
                  DNA := OpenDeskAcc(NameHolder);
                End;

      FileMenu: Begin
                  Case Item_No of
                    1: Finished := True;          {quit}
                  End;
                End;

      EditMenu: Begin
                  If Not SystemEdit(Item_no - 1) {if not for a desk accessory}
                  then
                    Case Item_No of
                      1: begin end;               {undo}
                    { 2:                           line divider}
                      3: TECut  (TextHandle);
                      4: TECopy (TextHandle);
                      5: TEPaste(TextHandle);
                    End;
                End;

    End;{case of Menu_No}

    HiliteMenu(0);               {unhilite after processing menu}
  end; {the If codeword <> 0}
End; {of ProcessMenu_in procedure}


{-------------------------------------------------------------------}
{----- These are procedures called from the main event loop  -------}

PROCEDURE DealwthMouseDowns(Event:EventRecord);
Var Location: integer;
    WindowPointedTo: WindowPtr;
    MouseLoc:Point;
    WindoLoc:integer;
Begin
  MouseLoc := Event.Where;
  WindoLoc := FindWindow(MouseLoc, WindowPointedTo);
  Case WindoLoc of

     inMenuBar: ProcessMenu_in(MenuSelect(MouseLoc));

     inSysWindow: SystemClick(Event,WindowPointedTo);

     inContent: If WindowPointedTo <> FrontWindow
                then SelectWindow(WindowPointedTo)
                else begin
                       GlobaltoLocal(MouseLoc);
                       TEClick(MouseLoc, False, TextHandle);
                     end;

     inGrow:    If WindowPointedTo <> FrontWindow
                then SelectWindow(WindowPointedTo)
                else begin {ReSizeWindow(WindowPointedTo,MouseLoc);} end;

     inDrag   :DragWindow(WindowPointedTo,MouseLoc,DragArea);

     inGoAway :If TrackGoAway(WindowPointedTo,MouseLoc) then
               begin
                 HideWindow(WindowPointedTo);
               end;

  End{ of case};
End;

{-----------------------------------------------------------------------------}

PROCEDURE DealwthKeyDowns(Event:EventRecord);
Var CharCode:char;
Begin
   CharCode:= CharStuff(Event.message).Chr0; {get low byte w/no processing}

  If BitAnd(Event.modifier,CmdKey) = CmdKey
   then
     begin  {key board command - probably a menu command}
       ProcessMenu_in(MenuKey(CharCode));
     end
   else
     begin
       TEKey(CharCode, TextHandle);
       Scroll_ifNecessary(TextHandle);
     end;
End;

{-----------------------------------------------------------------------------}

PROCEDURE DealwthActivates(Event: EventRecord);
Var TargetWindow:WindowPtr;
Begin
   TargetWindow := WindowPtr(Event.message);
 {  DrawGrowIcon(TargetWindow);  this window doesn't have one}

   If Odd(Event.modifiers) {then the window is becoming active}
   then
     begin
       SetPort(TargetWindow);
       TEActivate(TextHandle);
     end
   else
     begin
       TEDeactivate(TextHandle);
     end;
End;

{-----------------------------------------------------------------------------}

PROCEDURE DealwthUpdates(Event:EventRecord);
Var UpDateWindow:  WindowPtr;
        TempPort:  WindowPtr;
Begin
   UpDateWindow := WindowPtr(Event.message);
   GetPort(TempPort);                {Save the current port}

   SetPort    (UpDateWindow);      {set the port to one in Evt.msg}
   BeginUpDate(UpDateWindow);
{     EraseRect(UpDateWindow^.VisRgn^^.rgnBBox);}
     TEUpDate (DestRect, TextHandle);
     FrameText;
   EndUpDate  (UpDateWindow);

   SetPort    (TempPort);             {restore to the previous port}
End;

{-----------------------------------------------------------------------------}

PROCEDURE MainEventLoop;
Var Event:EventRecord;
    ProcessIt: Boolean;
Begin
  Repeat
    SystemTask;             {so you can support Desk Accessories}
    ChangeCursorForText;    {change cursor to I-Beam in text window}
    TEIdle(TextHandle);     {make insertion caret blink}

    ProcessIt := GetNextEvent(EveryEvent,Event);
    If ProcessIt{is true} then {we'll ProcessIt}
      Case Event.what of

        mouseDown  : DealwthMouseDowns(Event);
        AutoKey    : DealwthKeyDowns  (Event);
        KeyDown    : DealwthKeyDowns  (Event);
        ActivateEvt: DealwthActivates (Event);
        UpDateEvt  : DealwthUpdates   (Event);

      End;{of Case}
  Until Finished; {terminate the program}
End;

{-----------------------------------------------------------------------------}

PROCEDURE InitThings;
Begin
  InitGraf(@thePort);          {create a grafport for the screen}

  MoreMasters;                 {extra pointer blocks at the bottom of the heap}
  MoreMasters;                 {this is 5 X 64 master pointers}
  MoreMasters;
  MoreMasters;
  MoreMasters;

{get the cursors we use and lock them down - no clutter}
  ClockCursor := GetCursor(watchCursor);
  TextCursor  := GetCursor(iBeamCursor);
  HLock(Handle(ClockCursor));
  HLock(Handle(TextCursor));

{show the watch while we wait for inits & setups to finish}
  SetCursor(ClockCursor^^);

{init everything in case the app is the Startup App}
  InitFonts;                     {startup the fonts manager}
  InitWindows;                   {startup the window manager}
  InitMenus;                     {startup the menu manager}
  TEInit;                        {startup the text edit manager}
  InitDialogs(Nil);              {startup the dialog manager}

  Finished := False;             {set program terminator to false}
  FlushEvents(everyEvent,0);     {clear events from previous program}
End;

{-----------------------------------------------------------------------------}

PROCEDURE SetupLimits;
Begin
  Screen := ScreenBits.Bounds;   {set the size of the screen}
  SetRect(DragArea,Screen.left+4,Screen.top+24,Screen.right-4,Screen.bottom-4);
  SetRect(GrowArea,Screen.left,Screen.top+24,Screen.right,Screen.bottom);
End;

{-----------------------------------------------------------------------------}

PROCEDURE SetupMenus;
Var
  MenuTopic: MenuHandle;
Begin
  MenuTopic := GetMenu(AppleMenu);  {get the apple desk accessories menu}
  AddResMenu(MenuTopic,'DRVR');     {adds all names into item list}
  InsertMenu(MenuTopic,0);          {put in list held by menu manager}

  MenuTopic := GetMenu(FileMenu);   {always need this for Quiting}
  InsertMenu(MenuTopic,0);

  MenuTopic := GetMenu(EditMenu);   {always need for editing Desk Accessories}
  InsertMenu(MenuTopic,0);

  DrawMenuBar;           {all done so show the menu bar}
End;

{-----------------------------------------------------------------------------}

PROCEDURE SetupWindows;
Begin
  TextWindow :=  GetNewWindow(WindResID, Nil, POINTER(-1)); {get windo resource}
End;

{-----------------------------------------------------------------------------}

PROCEDURE SetupTextEdit;
Var LineHt:    Integer;
    FontStuff: FontInfo;
    ViewRect:  Rect;
Begin
  SetPort(TextWindow);                     {ActEvt has not occured, so set it}

  TextFont(Venice);                        {this is the font we'll use}
  TextSize(12);                            {this is the size we'll use}
  GetFontInfo(FontStuff);                  {get its specifics}

{get the lineheight for the font}
  LineHt := FontStuff.ascent + FontStuff.descent + FontStuff.leading;
  LineHt := (LineHt+1) * (MaxViewLines+1);

  SetRect(ViewRect,10,10,135,LineHt);      {initialize DestRect}
  TextFrame := ViewRect;                   {rect for framing the text}
  InSetRect(TextFrame, -1,-1);             {make it a little larger}
  DestRect := ViewRect;                    {make the DestRect the same}

  InSetRect(DestRect,9,0);                 {make Rect smaller by 2 pixels }

  TextHandle := TENew(DestRect, ViewRect); {allocate a text record for the text}

  TextHandle^^.txFont := Venice;           {set the font}
  TextHandle^^.txSize := 12;               {set the font size}

  LastLine := maxViewLines;                {start scrolling after max text lines}
End;

{-----------------------------------------------------------------------------}

PROCEDURE SetUpThings;
Begin
  SetupWindows;         {do first so its low in heap}
  SetupMenus;
  SetupTextEdit;
  SetupLimits;

  InitCursor;           {ready to go, so show the Arrow cursor}
End;

{-----------------------------------------------------------------------------}

PROCEDURE CloseThings;
Begin
{close files, if you changed sys resources, UNchange them here be carefull}
{about changing sys things, remember the Switcher could be around}

{why put these here??? who knows everything is gone anyway}
   TEDispose(TextHandle);          {get rid of TextEdit stuff}
   DisposeWindow(TextWindow);      {since W mgr allocated space}
End;

{-----------------------------------------------------------------------------}

BEGIN
  InitThings;
  SetUpThings;
  MainEventLoop;
  CloseThings;
END.
